所有参考资料都写在文末,侵删致歉。

1. 介绍

jq
stedolan/jq

jq is a lightweight and flexible command-line JSON processor.

jq 是一款命令行下处理 JSON 数据的工具。
其可以接受标准输入,命令管道或者文件中的 JSON 数据,经过一系列的过滤器(filters)和表达式的转后形成我们需要的数据结构并将结果输出到标准输出中。
jq 的这种特性使我们可以很容易地在 Shell 脚本中调用它。

2. 安装

Download jq

brew install jq
sudo apt-get install jq
yum -y  install epel-release   &&    yum -y install jq

手动编译

1
2
3
4
5
6
git clone https://github.com/stedolan/jq.git
cd jq
autoreconf -i
./configure --disable-maintainer-mode
make
sudo make install

3. jq表达式(filters)

jq Manual

在线测试

jq命令中文手册

$ jq
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Some of the options include:
-c compact instead of pretty-printed output;
-n use `null` as the single input value;
-e set the exit status code based on the output;
-s read (slurp) all inputs into an array; apply filter to it;
-r output raw strings, not JSON texts;
-R read raw strings, not JSON texts;
-C colorize JSON;
-M monochrome (don't colorize JSON);
-S sort keys of objects on output;
--tab use tabs for indentation;
--arg a v set variable $a to value <v>;
--argjson a v set variable $a to JSON value <v>;
--slurpfile a f set variable $a to an array of JSON texts read from <f>;

1. 格式化输出json

1
2
3
4
#xxx.JSON 中是我们要处理的 JSON 数据,我们可以直接将文件名传给 jq
$ jq -r '.' xxx.JSON
#或者由其他程序读出文件内容,并传给 jq
$ cat xxx.JSON|jq -r '.'
1
'-r'选项。该选项控制 jq 是输出 raw 格式内容或 JSON 格式内容。所谓的 JSON 格式是指符合 JSON 标准的格式。例如,假设我们要查询 JSON 字符串{"name":"tom"}中 name 的值. 使用-r 选项时返回的是'tom'. 不使用-r 选项时,返回的是'"tom"'.返回值多了一对双引号。

2. ‘.’ 符号

单独的一个'.'符号用来表示对作为表达式输入的整个 JSON 对象的引用。

3. JSON 对象操作

1
cat xxx.json|jq '.profile_image'

如果key不存在则返回null

4. 迭代器

1
cat xxx.json|jq '.[]'

该表达式的输入可以是数组或者 JSON 对象。输出的是基于数组元素或者 JSON 对象属性值的 iterator。

5. 访问属性值

输出数组元素或者 JSON 对象的属性值

1
cat xxx.json|jq '.[<value>]'

1
cat xxx.json|jq '.[<string>]'

6. 数组切片

1
cat xxx.json|jq '.[startindex:endindex]'

7. 表达式操作(‘,’和 ‘|’)

表达式操作是用来关联多个基础表达式。

其中逗号表示对同一个输入应用多个表达式。管道符表示将前一个表达式的输出用作后一个表达式的输入。
当前一个表达式产生的结果是迭代器时,会将迭代器中的每一个值用作后一个表达式的输入从而形成新的表达式。
例如'.[]|.+1', 在这个表达式中,第一个子表达式'.[]'在输入数组上构建迭代器,第二个子表达式则在迭代器的每个元素上加 1。

1
cat xxx.json|jq '.[] | .name'

8. 内置运算

Builtin operators and functions

  • 数学运算

对于数字类型,jq 实现了基本的加减乘除/和求余%运算。对于除法运算,jq 最多支持 16 位小数。

  • 字符串操作

    • 连接操作(运算符为+,例如:"tom "+"jerry"结果为"tom jerry")

    • 复制操作(例如:'a'*3结果为'aaa')

    • 字符串分割操作(将字符串按照指定的分割符分成数组,例如"sas"/"s"的结果为["","a",""],而"sas"/"a"的结果为["s","s"]

  • 数组操作

    • 并集'+'运算,结果数组中包含参与运算的数组的所有元素。
    • 差集运算'-',例如:有数组 a,b, a-b 的结果为所有在 a 中且不包含在 b 中的元素组成的数组。
  • 对象操作

    • jq 实现了两个 JSON 对象的合并操作(merge)。当两个参与运算的对象包含相同的属性时则保留运算符右侧对象的属性值。有两种合并运算符:+*。所不同的是,运算符’+’只做顶层属性的合并,运算符’*’则是递归合并。
    • 例如:有对象 a={"a":{"b":1}}, b={"a":{"c":2}}a+b 的结果为{"a":{"c":2}},而 a*b 的结果为{"a":{"b":1,"c":2}}

Conditionals and Comparisons

  • 比较操作
    jq 内部支持的比较操作符有==, !=,>,>=,<=<。其中,==的规则和 javascript 中的恒等(===)类似,只有两个操作数的类型和值均相同时其结果才是 true。

  • 逻辑运算符: and/or/not
    在 jq 逻辑运算中,除了 falsenull 外,其余的任何值都等同于 true

  • 默认操作符(//)
    表达式a//b表示当表达式 a 的值不是 falsenull 时,a//b 等于 a,否则等于 b。
  • 迭代器运算

类似笛卡尔积方式。

1
cat xxx.json|jq '([1,2]|.[])+([4,6]|.[])'

9. try-catch

1
cat xxx.json|jq 'try .a catch ". is not an object"'

1
cat xxx.json|jq '[.[]|try .a]'

10. 内置函数

  • map(x), map_values(x)

    1
    cat xxx.json|jq 'map(.+1)'

    1
    cat xxx.json|jq 'map_values(.+1)'

  • 过滤操作

    • 基于数据类型的过滤
      如表达式jq '.[]|numbers'的结果只包含数组。

      可以用来过滤的类型过滤器有:arrays, objects, iterables, booleans, numbers, normals, finites, strings, nulls, values, scalars

    • select 函数
      select 接受一个条件表达式作为参数。其输入可以是迭代器,或者和 map 函数配合使用来处理数组。当输入中的某个元素使 select 参数中的条件表达式结果为真时,则在结果中保留该元素,否则不保留该元素。

      1
      cat xxx.json|jq '.[] | select(.id == "second")'

  • 路径操作path(path_expression)
    和 xpath 类似,在 jq 中的 path 也是指从根到某个叶子属性的访问路径。
    在 jq 中有两种表示路径的方式:数组表示法和属性表示法。

    • 属性表示法类似于我们在 filter 中访问某个属性值的方式,如'.a.b'
    • 数组表示法是将路径中的每一部分表示为数组的一个元素。

    • jq 提供了一个内置函数 path 用来实现路径从属性表示法到数组表示法的转换。

    • 函数用来读取路径的值getpath, 设置路径的值setpath和删除路径del
      这三个函数对路径的处理并不一致。其中 getpathsetpath 只接受数组表示法的路径,而 del 函数只能正确处理属性表示法的路径。

    • 函数paths 用来枚举可能存在的路径。
      在没有参数的情况下,paths 函数将输出 JSON 数据中所有可能的路径。paths 函数可以接受一个过滤器,来只输出满足条件的路径。

  • 存在函数contains/inside

    1
    cat xxx.json|jq 'contains({foo: 12, bar: [{barp: 12}]})'

    1
    cat xxx.json|jq 'inside({"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]})'

  • 数组函数
    jq 提供内置函数用于完成数组的扁平化flatten,反序reverse,排序sort, sort_by,比较min,min_by,max,max_by和查找indices,indexrindex

    其中indices函数的输入数据可以是数组,也可以是字符串。和index 函数不同的是,其结果是一个包含所有参数在输入数据中位置的数组,具体请参看下面的例子。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    jq 中的数组函数
    #结果是[1,2,3,4]
    jq -nr '[1,[2,3],4]|flatten'
    #结果是[3,2,1]
    jq -nr '[1,2,3]|reverse'
    jq -nr '[3,1,2]|sort'
    jq -nr '[{"a":1},{"a":2}]|sort_by(.a)'
    #下面两个表达式的结果都是[1,3]
    jq -nr '"abcb"|indices("b")'
    jq -nr '[1,3,2,3]|indices(3)'

4. jq 高级特性

Advanced features

1. 变量

Variable / Symbolic Binding Operator: ... as $identifier | ...

jq 内部支持两种变量的定义方式。第一种可以通过命令行参数--arg定义。这种方式用来从外部(如:shell)传入数据以供 filter 表达式使用。

第二种方式,在 jq 表达式内部,我们可以自己声明变量用来保存表达式的结果以供表达式其余部分使用。

1
2
3
4
#在下面的表达式中变量$arraylen 用来保存数组长度,整个表达式结果为 4
jq -nr '[1,2,3]|length as $arraylen|$arraylen+1'
#可以同时定义多个变量
jq -nr '{"firstname":"tom","lastname":"clancy"}|. as {firstname:$fn, lastname:$ln}|"author is "+$fn+"*"+$ln'

变量作用域

1
2
3
4
5
6
7
8
#会抛出 arraylen 没定义的异常
jq -nr '[1,2,3]|(length as $arraylen|$arraylen)|$arraylen+1'

#正常执行,结果为 4.
jq -nr '[1,2,3]|(length as $arraylen|$arraylen+1)'

#函数作用域。该表达式会抛出异常,因为变量$fn 是在函数 fname 中定义,对最后一个子表达式##来说,$fn 是不可见的。
jq -nr '{"firstname":"tom","lastname":"clancy"}|def fname:. as {firstname:$fn, lastname:$ln}|$fn; fname|$fn'

2. 迭代器

  • Reduce
    reduce 关键字用来通过运算将迭代器的所有值合并为一个值。
    其调用形式为:reduce <itexp> as $var (INIT; UPDATE)
    其中,表达式 itexp 产生的迭代器被赋值给变量 var, UPDATE 是关于变量 var 的表达式。INIT 是该表达式的初始输入。相对于 itexp 结果中的每个元素,UPDATE 表达式被调用一次,计算出结果用作下一次 UPDATE 调用的输入。

    1
    cat xxx.json|jq 'reduce .[] as $item (0; . + $item)'

  • foreach
    关键字 foreach 的作用和 reduce 类似。
    其调用形式为 foreach EXP as $var (INIT; UPDATE; EXTRACT)
    reduce 关键字不同的是,foreach 关键字的每次迭代是先调用 UPDATE 再调用 EXTRACT,并以一个迭代器保留每一次的中间结果。该迭代器最后作为整个表达式的结果输出。

    1
    cat xxx.json|jq '[foreach .[] as $item ([[],[]]; if $item == null then [[],.[0]] else [(.[0] + [$item]),[]] end; if $item == null then .[1] else empty end)]'

  • 内置函数limit(n;exp)用来取得表达式exp结果的前 n 个值。
  • 内置函数 first, lastnth
    这几个函数用来取迭代器中某一个特定的元素。既可以以函数的形式调用,也可以作为子表达式调用。
    1
    2
    3
    4
    5
    6
    #下面的表达式按照函数的形式调用 first,结果为 1
    jq -nr 'first([1,2,3]|.[])'
    #下面的表达式以 filter 形式调用 first
    jq -nr '[1,2,3]|.[]|first'
    #nth 函数的使用,结果为 2
    jq -nr 'nth(1;[1,2,3]|.[])'

3. 函数定义

jq 提供了定义函数的能力。
其语法规则为:def funcname(arguments) : funcbodyexp;

定义函数时的规则:

函数名或者参数列表后面应该跟冒号以标志函数体开始。
如果不需要参数,可以直接把整个参数列表部分省去。
参数列表中,参数之间以分号(";")分隔。
函数体只能是一个表达式,且表达式需以分号结尾
如果在表达式内部定义函数,整个子表达式部分不能只包含函数定义,否则 jq 会抛出语法错误
1
cat xxx.json|jq 'def addvalue(f): . + [f]; map(addvalue(.[0]))'

1
cat xxx.json|jq 'def addvalue(f): f as $x | map(. + $x); addvalue(.[0])'

5. 参考资料

命令行 JSON 处理工具 jq 的使用介绍

Cookbook